package com.alex;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.profile.StackProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * DMH第一个测试
 *
 * Benchmark               Mode  Cnt  Score   Error  Units
 * JMHDemo1.arrayListAdd   avgt   10  0.026 ± 0.010  us/op
 * JMHDemo1.linkedListAdd  avgt   10  0.125 ± 0.110  us/op
 *
 * 可以看出运行的次数、花费的时间以及误差.
 *
 * 若一个JMH的类中,没有任何一个@Benchmark标注的方法, 会抛出异常.
 * 在Main中的OptionsBuilder配置的参数, 无法通过方法上的注解进行覆盖.
 *
 * BenchmarkMode：
 *  1.AverageTime：平均响应时间
 *  2.Throughput：方法吞吐量, 单位时间内可以对该方法调用多少次
 *  3.SampleTime：时间采样；采用一种抽样的方式来统计基准测试方法的性能结果;
 *  可以看出在不同区间，分布情况
 *  4.SingleShotTime：主要用来进行冷测试, 不论Warmup还是Measurement，在每一个批次中
 *  基准测试方法只会被执行一次, 一般情况下, 将Warmup的批次设置为0.
 *  5.All：设置多模式的方式运行基准测试方法.
 *
 *
 * OutputTimeUnit: 提供了统计结果的输出时间单位; 可以设置在类/方法/builder, 也会存在
 * 互相覆盖.
 *
 * Scope:
 *  1.Benchmark: 线程共享的State, 多个线程会共享一个实例.
 *  2.Thread: 线程独享的State, 每一个运行基准测试方法的线程都会持有一个独立的对象实例
 *  3.Group: 线程组共享的State; 同一个线程组会共享一个实例.
 *
 * Level:
 *  1.Trial: 该方法会在每一个基准测试方法的所有批次执行的前后被执行.
 *  2.Iteration：每一个基准测试批次执行的前后被执行.
 *  3.Invocation：每一个批次的度量过程中, 每一次对基准方法的调用前后都会执行.
 *
 * 注解解释
 *  1.@Threads(5): 会使用几个线程来测试一个基准测试方法.
 *  2.@GroupThreads(3): 线程组中有几个线程.
 *  3.@Group("test"): 定义线程组的名称.
 *  4.@Param({"1", "2", "3"}): 作用于属性上, 使得属性存在这么几种值.
 *  5.@Setup: 每一个基准测试方法执行前调用, 通常用于资源的初始化.
 *  6.@TearDown：每一个基准测试方法执行之后调用, 通常用于资源的回收清理.
 *  7.@CompilerControl(CompilerControl.Mode.EXCLUDE)：禁止JVM优化
 *  8.@Fork(1): 0的时候, 进程和JMH的类共享同样的进程profiler; 1的时候则为
 *  每一个基准测试方法开辟新的进程去运行.
 */

@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class JMHDemo1 {

    private final static String DATA = " DUMMY DATA";

    private List<String> arrayList;
    private List<String> linkedList;

    @Setup(Level.Iteration)
    public void setUp() {
        this.arrayList = new ArrayList<String>();
        this.linkedList = new LinkedList<String>();
    }


    @Benchmark// 基准测试的注解, 否则只算作普通的方法
    public List<String> arrayListAdd() {
        this.arrayList.add(DATA);
        return arrayList;
    }

    @Benchmark
    public List<String> linkedListAdd() {
        this.linkedList.add(DATA);
        return linkedList;
    }

    public static void main(String[] args) throws RunnerException {
        final Options opts = new OptionsBuilder()
                .include(JMHDemo1.class.getSimpleName())
                .forks(1)
                // WarmUp(预热), 在做基准测试代码正式度量之前, 先对其进行预热,
                // 使得代码的执行是经历过类的早期优化、JVM运行期编译、JIT优化之后
                // 的最终状态, 从而能够获得代码真实的性能数据
                // 可在类/方法上增加 @Warmup(iterations = 3)
                .warmupIterations(10)
                // 真正的度量操作, 这个数据才会被纳入统计之中, 预热的数据不会纳入统计
                // 可在类/方法上增加@Measurement(iterations = 5)
                .measurementIterations(10)
                .addProfiler(StackProfiler.class)
                .build();

        new Runner(opts).run();
    }
}
